/*!
 * jQueryCSSClock - jQuery JavaScript Library Plug-in
 * http://code.google.com/p/jquery-snoop/
 *
 * Copyright (c) 2010 Lucas Nascimento Arruda
 * Licensed under the MIT license.
 * http://en.wikipedia.org/wiki/MIT_License
 *
 * Date: (Thu, 09 June 2011)
 *
 * @requires jQuery 1.3+
 * @author Lucas Arruda <lucas> at hostcampinas.com.br
 * @extends jQuery
 */

(function( $ ){
  // Verify if jQuery is loaded
  if (typeof(jQuery) != 'function') {
    alert('jQueryCSSClock: jQuery is not properly loaded.');
  } 
  else {
    var ver = jQuery.fn.jquery.split('.');
    // Verify if jQuery's version is newer than 1.3
    if (ver[0] < 1 || ver[1] < 3) {
      alert("jQueryCSSClock: jQuery incompatible version.\nPlease use jQuery 1.3+");
    }
    else {
      /*
       * jQueryCSSClock JSON Methods and Properties
       */
      var jQueryCSSClock = {
        // The ID of the calling DOM 
        // Not necessarily a unique ID, can be a class
        // or any selector that macthes a valid CSSClock element
        sender : null,
        // The last time the clock was checked for an update need
        g_nLastTime : null,
        // This instance settings
        settings : null,
        // A pool of clock instances to allow
        // multiple and individual clocks on the same window
        pool : {},
        // setInterval thread is placed here
        thread : null,
        
        /*
         * Implementation of init()
         * Clear some variables and initiate the clock process
         */
        init: function() {
          // Erase the "last time" so a new clock is coming up 
          this.g_nLastTime = null,
          this.update();
        },
        
        /*
         * Implementation of record()
         * Adds a record on the clock pool
         */
        record: function() {
          this.pool[this.sender] = {
            "objDate" : this.g_nLastTime,
            "settings" : this.settings
          };
        },
        
        /*
         * Implementation of update()
         * Does all the logic calculations related to time and calls
         * other essentials methods like 'show' and 'wait'
         */
        update: function() { 
          // This date object is essential for doing calcs, diffs...
          var objDate = new Date(); 
          // Firstly, check if all calcs are really necessary.
          if (this.check(objDate)) { 
            // Some parts of this script were taken from this:
            // http://www.sohtanaka.com/web-design/css-clock-a-css-javascript-experiment/
            // get the time
            var nHour = objDate.getUTCHours(); 
            nHour += this.settings.GMT; 
            objDate.setHours(nHour);
            
            if (nHour > 12) { 
              nHour -= 12; // switch from 24-hour to 12-hour system
            }
            var nMinutes = objDate.getUTCMinutes(); 
            
            // round the time
            var nRound = 5;
            var nDiff = nMinutes % nRound;
            if (nDiff != 0) {
              if (nDiff < 3) { 
                nMinutes -= nDiff; // round down
              } 
              else { 
                nMinutes += (nRound - nDiff); // round up
              } 
            }
            if (nMinutes > 59) {
              // handle increase the hour instead
              nHour++;
              nMinutes = 0;
            } 
            
            // Keep the date for post recording
            this.g_nLastTime = objDate; 
            // Show/Updates the clocks on the screen
            this.show(nHour, nMinutes);
            // Store the changes on pool
            this.record();
            // Put it to wait until interval has passed and check it over again
            this.wait();
          }
        },
        
        /*
         * Implementation of show()
         * Changes the clock DOM classes so the images change and the pointers actually "move".
         * @param nHour (Integer): The rounded hour
         * @param nMinutes (Integer): The rounded minute
         */
        show: function(nHour, nMinutes) {
          // Update the on page elements
          if (this.exists()) {
            // Get the old classes
            var hr_class = $(this.sender + " .hours").attr('class').match(/hr\d+/);
            var min_class = $(this.sender + " .minutes").attr('class').match(/min\d+/);
            // And remove them if they already exists
            if (hr_class != null && min_class != null) {
              $(this.sender + " .hours").removeClass(hr_class[0]);
              $(this.sender + " .minutes").removeClass(min_class[0]);
            }
            // Now set the new classes
            $(this.sender + " .hours").addClass('hr' + nHour);
            $(this.sender + " .minutes").addClass('min' + nMinutes);
          }
        },
        
        /*
         * Implementation of wait()
         * Sets the thread that will check for changes each set interval
         */
        wait: function() { 
          if (this.thread == null) {
            // Set a timer to call this function again
            var clock = this;
            this.thread = window.setInterval(function() { 
              clock.search()
            }, clock.settings.interval);
          }
        },
        
        /*
         * Implementation of search()
         * Go through each clock on the pool and set the jQueryCSSClock object
         * settings as theirs, then call the update method.
         */
        search: function() { 
          // We do not need a thread running if no clocks are on the screen anymore.
          // Here we disable it if the pool is empty.
          if ($.isEmptyObject(this.pool)) {
             this.thread = null;
          }
          else {
            var clock = this;
            // Iterate over the clocks on the pool
            $.each(this.pool, function(i, o) {
              clock.sender = i; 
              if (clock.exists()) {
                clock.settings =  o.settings;
                clock.g_nLastTime = o.objDate;
                clock.update(); 
              }
              else {
                // If the clock does not exists anymore lets remove it from the pool
                delete clock.pool[i];
              }
            });
          }
        },
        
        /*
         * Implementation of check()
         * Validates if the clock is about to be created or if
         * The last recorded time is more (or equals) than the 
         * actual time plus 5 minutes.
         */
        check: function(objDate) {  
          return (!this.g_nLastTime
                  || this.g_nLastTime.getHours() < (objDate.getUTCHours() + this.settings.GMT)
                  || this.g_nLastTime.getMinutes() <= (objDate.getMinutes() - 5));
        },
        
        /*
         * Implementation of exists()
         * Checks if the actual clock and their pointers are still in the DOM
         */
        exists: function() {
          return ($(this.sender).length && $(this.sender + " .hours").length && $(this.sender + " .minutes").length);
        }
      };
      // End of jQueryCSSClock JSON Methods and Properties
      
      /*
       * Implementation of cssClock()
       * Call this to use the plug-in!
       * @param custom_settings {JSON}: The plugin options in JSON format
       */
      $.fn.cssClock = function(custom_settings) {
        // These are the default settings
        var default_settings = {
          'GMT'		   : 0,
          'interval' : 60000
        };
        // If options exist, lets merge them
        // with our default settings
        if (custom_settings) {
          $.extend(default_settings, custom_settings);
        }
        // Keep the settings
        jQueryCSSClock.settings = default_settings;
        // Keep the DOM object that wants the clock
        jQueryCSSClock.sender = sender = this.selector; 
        // Goes on only if the following DOM exists:
        // <tag id_or_class="clock_name">
        //   <tag class="hours"></tag>
        //   <tag class="minutes"></tag>
        // </tag>
        if (jQueryCSSClock.exists()) { 
          jQueryCSSClock.init();
        }        
      };
    }
  }
})( jQuery );
